#include "config.h"
//#include <windows.h>
#include <stdio.h>
#include <string.h>

#include "aux_types.h"
#include "aux_errno.h"
#include "aux_socket.h"
#include "aux_string.h"

#define IN6ADDRSZ 16
#define INT16SZ 2


typedef struct
{
    int nBase;
    int nLen;
} TBaseLen;



static const char *  inet_ntop4(const u8 *pabySrc,
                                    char *pDstBuf, size_t nSize)
{
    static const char szFmt[] = "%u.%u.%u.%u";
    const u32 nBuffLen = sizeof("255.255.255.255");
    char strBuff[sizeof("255.255.255.255")];
    int nLen;

    if (!pabySrc || !pDstBuf || !nSize)
        return (NULL);


    nLen = aux_snprintf(strBuff, nBuffLen, szFmt,
                    pabySrc[3], pabySrc[2], pabySrc[1], pabySrc[0]);


    if (nLen <= 0 || ((size_t)nLen) >= nSize)
        return (NULL);
    memset(pDstBuf, 0, nSize);
    strncpy(pDstBuf, strBuff, nLen);
    return (pDstBuf);
}

static const char *  inet_ntop6(const u8 *pabySrc, char *pDstBuffer,
                                    size_t nSize)
{
    char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
    char *tp, *ep;
    TBaseLen tBest, tCur;
    u32 adwWords[IN6ADDRSZ / INT16SZ];
    s32 nIndex = 0;
    int advance;

    if (!pabySrc || !pDstBuffer || !nSize)
        return NULL;

    memset(adwWords, '\0', sizeof(adwWords));

    for (nIndex = 0; nIndex < IN6ADDRSZ; ++nIndex)
        adwWords[nIndex / 2] |= (pabySrc[nIndex] << ((1 - (nIndex % 2)) << 3));

    tBest.nBase = -1;
    tCur.nBase  = -1;
    for (nIndex = 0; nIndex < (IN6ADDRSZ / INT16SZ); ++nIndex)
    {
        if (adwWords[nIndex] == 0)
        {
            if (tCur.nBase == -1)
            {
                tCur.nBase = nIndex;
                tCur.nLen  = 1;
            }
            else
            {
                tCur.nLen++;
            }
        }
        else
        {
            if (tCur.nBase != -1)
            {
                if (tBest.nBase == -1 || tCur.nLen > tBest.nLen)
                    tBest = tCur;

                tCur.nBase = -1;
            }
        }
    }

    if (tCur.nBase != -1)
    {
        if (tBest.nBase == -1 || tCur.nLen > tBest.nLen)
            tBest = tCur;
    }

    if (tBest.nBase != -1 && tBest.nLen < 2)
        tBest.nBase = -1;

    tp = tmp;
    ep = tmp + sizeof(tmp);
    for (nIndex = 0; nIndex < (IN6ADDRSZ / INT16SZ) && tp < ep; ++nIndex)
    {
        /** Are we inside the best run of 0x00's? */
        if (tBest.nBase != -1 && nIndex >= tBest.nBase &&
            nIndex < (tBest.nBase + tBest.nLen))
        {
            if (nIndex == tBest.nBase)
            {
                if (tp + 1 >= ep)
                    return (NULL);

                *tp++ = ':';
            }
            continue;
        }
        /** Are we following an initial run of 0x00s or any real hex? */
        if (nIndex != 0)
        {
            if (tp + 1 >= ep)
                return (NULL);

            *tp++ = ':';
        }
        /** Is this address an encapsulated IPv4? */
        if (nIndex == 6 && tBest.nBase == 0 &&
            (tBest.nLen == 6 || (tBest.nLen == 5 && adwWords[5] == 0xffff)))
        {
            if (!inet_ntop4(pabySrc + 12, tp, (size_t)(ep - tp)))
                return (NULL);

            tp += strlen(tp);
            break;
        }

        advance = aux_snprintf(tp, ep - tp, "%x", adwWords[nIndex]);
        if (advance <= 0 || advance >= ep - tp)
            return (NULL);

        tp += advance;
    }

    /** Was it a trailing run of 0x00's? */
    if (tBest.nBase != -1 && (tBest.nBase + tBest.nLen) == (IN6ADDRSZ / INT16SZ))
    {
        if (tp + 1 >= ep)
            return (NULL);
        *tp++ = ':';
    }

    if (tp + 1 >= ep)
        return (NULL);

    *tp++ = '\0';

    /**
     * Check for overflow, copy, and we're done.
     */
    if ((size_t)(tp - tmp) > nSize)
        return (NULL);

    aux_strlcpy(pDstBuffer, tmp, nSize);
    return (pDstBuffer);
}


static const char *  s_inet_ntop(int af, const void *src,
                                      char *dst, size_t size)
{
    switch (af)
    {
        case AF_INET:
            return (inet_ntop4((const u8 *)src, dst, size));
        case AF_INET6:
            return (inet_ntop6((const u8 *)src, dst, size));
        default:
            return (NULL);
    }
    /** NOTREACHED */
}


/*
 * WARNING: Don't even consider trying to compile this on a system where
 * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
 */

#define INADDRSZ    4
static int  inet_pton4(const char *src, unsigned char *dst);
static int  inet_pton6(const char *src, unsigned char *dst);

/* int
 * inet_pton(af, src, dst)
 *  convert from presentation format (which usually means ASCII printable)
 *  to network format (which is usually some kind of binary format).
 * return:
 *  1 if the address was valid for the specified address family
 *  0 if the address wasn't valid (`dst' is untouched in this case)
 *  -1 if some other error occurred (`dst' is untouched in this case, too)
 * author:
 *  Paul Vixie, 1996.
 */
static int
inet_pton(int af, const char *src, void *dst)
{
    switch (af) {
    case AF_INET:
        return (inet_pton4(src, (unsigned char *)dst));
    case AF_INET6:
        return (inet_pton6(src, (unsigned char *)dst));
    default:
        return (-1);
    }
    /* NOTREACHED */
}

/* int
 * inet_pton4(src, dst)
 *  like inet_aton() but without all the hexadecimal and shorthand.
 * return:
 *  1 if `src' is a valid dotted quad, else 0.
 * notice:
 *  does not touch `dst' unless it's returning 1.
 * author:
 *  Paul Vixie, 1996.
 */
static int
inet_pton4(const char *src, unsigned char *dst)
{
    static const char szDigits[] = "0123456789";
    int               nFindDigitNumber;
    int               nOctets;
    char              cChar;
    u8                tmp[INADDRSZ], *tp;

    nFindDigitNumber = 0;
    nOctets = 0;
    *(tp = tmp) = 0;

    while ((cChar = *src++) != '\0')
    {
        const char *pch;

        if ((pch = strchr(szDigits, cChar)) != NULL)
        {
            u32 nOneNum = *tp * 10 + (pch - szDigits);

            if (nOneNum > 255)
                return (0);

            if (! nFindDigitNumber)
            {
                if (++nOctets > 4)
                    return (0);

                nFindDigitNumber = 1;
            }

            *tp = nOneNum;
        }
        else if ('.' == cChar && nFindDigitNumber)
        {
            if (nOctets == 4)
                return (0);

            *++tp = 0;
            nFindDigitNumber = 0;
        }
        else
            return (0);
    }

    if (nOctets < 4)
        return (0);

    memcpy(dst, tmp, INADDRSZ);
    return (1);
}

/* int
 * inet_pton6(src, dst)
 *  convert presentation level address to network order binary form.
 * return:
 *  1 if `src' is a valid [RFC1884 2.2] address, else 0.
 * notice:
 *  does not touch `dst' unless it's returning 1.
 * credit:
 *  inspired by Mark Andrews.
 * author:
 *  Paul Vixie, 1996.
 */
static int
inet_pton6(const char *src, unsigned char *dst)
{
    static const char xdigits_l[] = "0123456789abcdef",
                      xdigits_u[] = "0123456789ABCDEF";
    unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
    const char    *xdigits, *curtok;
    int           ch, saw_xdigit, count_xdigit;
    unsigned int  val;

    if (!src)
        return (0);

    memset((tp = tmp), '\0', IN6ADDRSZ);
    endp = tp + IN6ADDRSZ;
    colonp = NULL;

    /* Leading :: requires some special handling. */
    if (':' == *src)
        if (*++src != ':')
            return (0);

    curtok = src;
    saw_xdigit = count_xdigit = 0;
    val = 0;

    while ((ch = *src++) != '\0')
    {
        const char *pch;

        if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
            pch = strchr((xdigits = xdigits_u), ch);

        if (pch != NULL)
        {
            if (count_xdigit >= 4)
                return (0);

            val <<= 4;
            val |= (pch - xdigits);

            if (val > 0xffff)
                return (0);

            saw_xdigit = 1;
            count_xdigit++;
            continue;
        }

        if (':' == ch)
        {
            curtok = src;
            if (!saw_xdigit)
            {
                if (colonp)
                    return (0);

                colonp = tp;
                continue;
            }
            else if (*src == '\0')
            {
                return (0);
            }

            if (tp + INT16SZ > endp)
                return (0);

            *tp++ = (unsigned char) (val >> 8) & 0xff;
            *tp++ = (unsigned char) val & 0xff;
            saw_xdigit = 0;
            count_xdigit = 0;
            val = 0;
            continue;
        }

        if ('.' == ch && ((tp + INADDRSZ) <= endp) &&
            inet_pton4(curtok, tp) > 0)
        {
            tp += INADDRSZ;
            saw_xdigit = 0;
            count_xdigit = 0;
            break;  /* '\0' was seen by inet_pton4(). */
        }

        return (0);
    }

    if (saw_xdigit)
    {
        if (tp + INT16SZ > endp)
            return (0);

        *tp++ = (unsigned char) (val >> 8) & 0xff;
        *tp++ = (unsigned char) val & 0xff;
    }

    if (colonp != NULL)
    {
        /*
         * Since some memmove()'s erroneously fail to handle
         * overlapping regions, we'll do the shift by hand.
         */
        const int n = tp - colonp;
        int i;

        if (tp == endp)
            return (0);

        for (i = 1; i <= n; i++)
        {
            endp[- i] = colonp[n - i];
            colonp[n - i] = 0;
        }

        tp = endp;
    }

    if (tp != endp)
        return (0);

    memcpy(dst, tmp, IN6ADDRSZ);
    return (1);
}

EErrNo aux_chk_ipv4_addr_str(const char *pszIp)
{
    u8  abyTmp[INADDRSZ * 2];

    if (!pszIp)
        return -1;

    return (1 == inet_pton(AF_INET, pszIp, abyTmp)) ? ERRNO_TRUE : ERRNO_FALSE;
}

EErrNo aux_chk_ipv6_addr_str(const char *pszIp)
{
    u8  abyTmp[IN6ADDRSZ * 2];

    if (!pszIp)
        return -ERRNO_EPARAMETER_INVALID;

    return (1 == inet_pton(AF_INET6, pszIp, abyTmp)) ? ERRNO_TRUE : ERRNO_FALSE;
}

EErrNo aux_ipv4_num2str(u32 nIp, char *pszIp, u8 nSize)
{
    u8 abyBuffer[] = {(u8)(0xff & (nIp >> 24)),
                      (u8)(0xff & (nIp >> 16)),
                      (u8)(0xff & (nIp >> 8)),
                      (u8)(0xff & (nIp)),
                     };

    return NULL == s_inet_ntop(AF_INET, &(abyBuffer[0]), pszIp, nSize) ?
           ERRNO_FAIL : ERRNO_SUCCESS;
}


